package vn.bang.xiangqi.components;

import java.util.Collection;

import vn.bang.xiangqi.infrastructure.Position;
import vn.bang.xiangqi.infrastructure.Vector;

public abstract class AbstractBaseNode implements INode {
	protected Position position;

	protected abstract boolean notPrevented(Vector vector,
			Collection<INode> ours, Collection<INode> enemies);

	protected abstract Vector retrieveVectorMovingTo(
			Position destinationPosition);

	public AbstractBaseNode() {
		this.position = new Position();
	}

	public AbstractBaseNode(Position originalPosition) {
		this.position = originalPosition;
	}

	public boolean MoveTo(Position destinationPosition, Collection<INode> ours,
			Collection<INode> enemies) {
		Vector vector = retrieveVectorMovingTo(destinationPosition);

		if (null == vector) {
			return false;
		}

		if (notPrevented(vector, ours, enemies) && notKillOurs(vector, ours)) {
			putDownAndKill(destinationPosition, enemies);
			return true;
		}
		return false;
	}

	public void Remove() {
		if (IsAlive()) {
			position.setRowIndex(0);
			position.setColumnIndex(0);
		}
	}

	public boolean PositionAt(Position position) {
		return this.position.equals(position);
	}

	public Position GetCurrentPosition() {
		if (null == position) {
			position = new Position();
		}
		return position;
	}

	public boolean IsAlive() {
		if (0 != position.getRowIndex() || 0 != position.getColumnIndex()) {
			return true;
		}
		return false;
	}

	protected Vector retrieveVectorValid(Position destinationPosition,
			Vector[] vectors) {
		for (int i = 0; i < vectors.length; i++) {
			if (position.getColumnIndex() + vectors[i].getColumn() == destinationPosition
					.getColumnIndex()
					&& position.getRowIndex() + vectors[i].getRow() == destinationPosition
							.getRowIndex()) {
				return vectors[i];
			}
		}
		return null;
	}

	protected Vector retrieveVectorValidInOurBarrack(
			Position destinationPosition, Vector[] vectors) {
		Vector vector = null;

		if (4 <= destinationPosition.getColumnIndex()
				&& destinationPosition.getColumnIndex() <= 6
				&& 1 <= destinationPosition.getRowIndex()
				&& destinationPosition.getRowIndex() <= 3) {
			vector = retrieveVectorValid(destinationPosition, vectors);
		}

		return vector;
	}

	protected boolean between(Position considerPosition, Vector vector) {
		if (0 == vector.getColumn()) {
			if (position.getColumnIndex() == considerPosition.getColumnIndex()) {
				int distance = considerPosition.getRowIndex()
						- position.getRowIndex();
				if (distance * vector.getRow() > 0) // The same sign.
				{
					if (Math.abs(vector.getRow()) > Math.abs(distance)) {
						return true;
					}
				} else // Different sign.
				{
					return false;
				}
			}
		} else {
			if (position.getRowIndex() == considerPosition.getRowIndex()) {
				int distance = considerPosition.getColumnIndex()
						- position.getColumnIndex();
				if (distance * vector.getColumn() > 0) // The same sign.
				{
					if (Math.abs(vector.getColumn()) > Math.abs(distance)) {
						return true;
					}
				} else // Different sign.
				{
					return false;
				}
			}
		}

		return false;
	}

	protected boolean hasNodeBetween(Vector vector, Collection<INode> ours,
			Collection<INode> enemies) {
		for (INode node : ours) {
			return between(node.GetCurrentPosition(), vector);
		}
		for (INode enemy : enemies) {
			return between(enemy.GetCurrentPosition(), vector);
		}
		return false;
	}

	protected INode retrieveNodeAtPosition(Position passedPosition,
			Collection<INode> nodes) {
		INode nodeResult = null;
		for (INode node : nodes) {
			if (node.PositionAt(passedPosition)) {
				nodeResult = node;
				break;
			}
		}

		return nodeResult;
	}

	private void putDownAndKill(Position destinationPosition,
			Collection<INode> enemies) {
		// Put down a new position.
		position.setColumnIndex(destinationPosition.getColumnIndex());
		position.setRowIndex(destinationPosition.getRowIndex());

		// Kill a enemy if any.
		INode enemyNeedKilled = retrieveNodeAtPosition(destinationPosition,
				enemies);
		if (null != enemyNeedKilled) {
			enemyNeedKilled.Remove();
		}
	}

	private boolean notKillOurs(Vector vector, Collection<INode> ours) {
		Position newPosition = new Position(position.getColumnIndex()
				+ vector.getColumn(), position.getRowIndex() + vector.getRow());

		for (INode node : ours) {
			if (node != this && node.PositionAt(newPosition)) // Not current
																// node AND the
																// same with new
																// position.
			{
				return false;
			}
		}
		return true;
	}

}
